
// **********************
// PI 2008 - Symbios
// **********************

//**********************************************************
//************   EXTRACTION DE FOND (EDF) ******************
//**********************************************************

//simple method of learning background scenes and segmenting fore-
//ground objects. It will work well only with scenes that do not contain moving background
//components (like a waving curtain or waving trees). It also assumes that the lighting
//remains fairly constant (as in indoor static scenes)

//**********************************************************
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <math.h>


//Data
//we create pointers for the various scratch and statistics-keeping images we will
//need along the way
IplImage *IavgF,*IdiffF, *IprevF, *IhiF, *IlowF;
IplImage *Iscratch,*Iscratch2;

//Float, 1-channel images
//
IplImage *Igray1,*Igray2, *Igray3;
IplImage *Ilow1, *Ilow2, *Ilow3;
IplImage *Ihi1,   *Ihi2, *Ihi3;
// Byte, 1-channel image
//
IplImage *Imaskt;
//Counts number of images learned for averaging later.
//
float Icount;

void setHighThreshold( float scale )
{
   cvConvertScale( IdiffF, Iscratch, scale,0 );
   cvAdd( Iscratch, IavgF, IhiF ,NULL);
   cvSplit( IhiF, Ihi1, Ihi2, Ihi3, 0 );
}
void setLowThreshold( float scale )
{
   cvConvertScale( IdiffF, Iscratch, scale,0 );
   cvSub( IavgF, Iscratch, IlowF ,NULL);
   cvSplit( IlowF, Ilow1, Ilow2, Ilow3, 0 );
}




//we create a single call to allocate all the necessary intermediate images
// I is just a sample image for allocation purposes
// (passed in for sizing)
//
void AllocateImages( IplImage* I ){
   CvSize sz = cvGetSize( I );
   IavgF     = cvCreateImage( sz, IPL_DEPTH_32F,  3 );
   IdiffF    = cvCreateImage( sz, IPL_DEPTH_32F,  3 );
   IprevF    = cvCreateImage( sz, IPL_DEPTH_32F,  3 );
   IhiF      = cvCreateImage( sz, IPL_DEPTH_32F,  3 );
   IlowF     = cvCreateImage( sz, IPL_DEPTH_32F,  3 );
   Ilow1     = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   Ilow2     = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   Ilow3     = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   Ihi1      = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   Ihi2      = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   Ihi3      = cvCreateImage( sz, IPL_DEPTH_32F,  1 );
   cvZero( IavgF );
   cvZero( IdiffF );
   cvZero( IprevF );
   cvZero( IhiF );
   cvZero( IlowF );
   Icount    = 0.00001; //Protect against divide  by zero
   Iscratch = cvCreateImage(   sz, IPL_DEPTH_32F, 3 );
   Iscratch2 = cvCreateImage(  sz, IPL_DEPTH_32F, 3 );
   Igray1    = cvCreateImage(  sz, IPL_DEPTH_32F, 1 );
   Igray2    = cvCreateImage(  sz, IPL_DEPTH_32F, 1 );
   Igray3    = cvCreateImage(  sz, IPL_DEPTH_32F, 1 );
   Imaskt    = cvCreateImage(  sz, IPL_DEPTH_8U,  1 );
   cvZero( Iscratch );
   cvZero( Iscratch2 );
}


void accumulateBackground( IplImage *I ){
   static int first = 1;                   // nb. Not thread safe
   cvCvtScale( I, Iscratch, 1, 0 );      // convert to float
   if( !first ){
      cvAcc( Iscratch, IavgF ,NULL);
      cvAbsDiff( Iscratch, IprevF, Iscratch2 );
      cvAcc( Iscratch2, IdiffF ,NULL);
      Icount += 1.0;
   }
   first = 0;
   cvCopy( Iscratch, IprevF ,NULL);
}


void createModelsfromStats() {
    cvConvertScale( IavgF, IavgF,( double)(1.0/Icount),0 );
    cvConvertScale( IdiffF, IdiffF,(double)(1.0/Icount),0 );
    //Make sure diff is always something
    cvAddS( IdiffF, cvScalar( 1.0, 1.0, 1.0,1.0), IdiffF ,NULL);
    setHighThreshold( 7.0 );
    setLowThreshold( 6.0 );
}



// Create a binary: 0,255 mask where 255 means foreground pixel
// I        Input image, 3-channel, 8u
// Imask Mask image to be created, 1-channel 8u
//
void backgroundDiff( IplImage *I,IplImage *Imask) {
  cvCvtScale(I,Iscratch,1,0); // To float;
  cvSplit( Iscratch, Igray1,Igray2,Igray3, 0 );
       //Channel 1
       cvInRange(Igray1,Ilow1,Ihi1,Imask);
  //Channel 2
  //
  cvInRange(Igray2,Ilow2,Ihi2,Imaskt);
  cvOr(Imask,Imaskt,Imask,NULL);
  //Channel 3
  //
  cvInRange(Igray3,Ilow3,Ihi3,Imaskt);
  cvOr(Imask,Imaskt,Imask,NULL);
  //Finally, invert the results
  //
  cvSubRS( Imask, cvScalar(255,255,255,255), Imask,NULL);
}

void DeallocateImages()
{
   cvReleaseImage( &IavgF);
   cvReleaseImage( &IdiffF );
   cvReleaseImage( &IprevF );
   cvReleaseImage( &IhiF );
   cvReleaseImage( &IlowF );
   cvReleaseImage( &Ilow1 );
   cvReleaseImage( &Ilow2 );
   cvReleaseImage( &Ilow3 );
   cvReleaseImage( &Ihi1 );
   cvReleaseImage( &Ihi2 );
   cvReleaseImage( &Ihi3 );
   cvReleaseImage( &Iscratch );
   cvReleaseImage( &Iscratch2 );
   cvReleaseImage( &Igray1 );
   cvReleaseImage( &Igray2 );
   cvReleaseImage( &Igray3 );
   cvReleaseImage( &Imaskt);
}


int tracking() {

//Capture CAM
CvCapture* capture = cvCaptureFromCAM( CV_CAP_ANY );
if( !capture ) {
    fprintf( stderr, "ERROR: capture is NULL \n" );
    getchar();
    return -1;
    }



cvNamedWindow( "mywindow", CV_WINDOW_AUTOSIZE );
cvNamedWindow( "mywindow2", CV_WINDOW_AUTOSIZE );
cvNamedWindow( "mywindow3", CV_WINDOW_AUTOSIZE );
IplImage* fond = cvQueryFrame( capture );
AllocateImages(fond);



cvWaitKey(0);

//Apprentissage du fon
int n_modele=0;
for( n_modele=0; n_modele<40; n_modele++) {
	fond = cvQueryFrame( capture );
	if( !fond ) {
		fprintf( stderr, "ERROR: frame is null...\n" );
		getchar();
		break;
		}
     	cvShowImage( "mywindow", fond );
	accumulateBackground(fond);
     	if( (cvWaitKey(10) & 255) == 27 ) break;

	}

createModelsfromStats();


IplImage* result =  cvCreateImage( cvGetSize(fond), 8 , 1 );
cvWaitKey(0);
//Soustraction de fond
int compteur=0;
int compteur2=0;
int compteur_result=0;
int x_previous=0;
int y_previous=0;
int x_result=0;
int y_result=0;

while( 1) {
	//Recuperer l image courante
	IplImage* frame = cvQueryFrame( capture );
	if( !frame ) {
		fprintf( stderr, "ERROR: frame is null...\n" );
		getchar();
		break;
		}
	//Soustraction et seuillage
	backgroundDiff( frame, result);
	cvDilate( result, result, NULL, 1);			//sur une fenetre 3x3 mais le pixel à central à max des pixels de la fntre
	//interface
	cvRectangle(frame,  cvPoint(405,1), cvPoint(640,400), cvScalar(0,255,0,0), 1,8,0);
	cvRectangle(frame,  cvPoint(1,1), cvPoint(400,300), cvScalar(0,0,255,0), 1,8,0);
	//reperage du doigt
	int y,x;							// coo. pour la boucle
	compteur=0;							// compte le nombre de pixel blanc aligné horizontalement
	compteur2=0;						// pas utilisé
	x_previous=x_result;				// retenir les resultat image precedente
	y_previous=y_result;				// retenir les resultat image precedente
	compteur_result=0;
	x_result=0;											// coo x resultat de la recherche du doigt
	y_result=0;											// coo y resultat de la recherche du doigt
	for ( y=0; y< 480; y++) {							// 400, result->height=640
		for (x=0; x< 400;x++) {
			uchar* ptr = (uchar *)(result->imageData + y * result->widthStep +x);		//page 44 OpenCV book
			if ( *ptr == 255 ) compteur ++;					// on compte le nombre de pixel blanc aligné horizontalement
			if ( *ptr == 0 ) { compteur2 ++, compteur=0; }			//pas utilisé
			if ( compteur == 4 && compteur_result==0 ){				//si on détecte 4 pixels alignés on a le doigt
					compteur_result = compteur;						// resultat du compte
					x_result=x;										//resultat : coo x du doigt
					y_result=y;										//resultat : coo y du doigt
				}
			}
		}
	if (compteur_result != 0)
		{
			//afficher un rectangle au niveau du doigt
       		cvRectangle(frame,  cvPoint(x_result-20,y_result-5), cvPoint(x_result+20,y_result+60), cvScalar(255,0,0,0), 1,8,0);
		}
	else { x_result =0; y_result=0; };

	IplImage* pointeur = cvCreateImage( cvSize(400,300), 8,3);
	cvCircle (pointeur, cvPoint(400-x_result,y_result),10,cvScalar(255,0,0,0),CV_FILLED,8,0);

	//Affichage image
	cvShowImage( "mywindow", frame );
	cvShowImage( "mywindow2", result );
	cvShowImage( "mywindow3", pointeur );

	if( (cvWaitKey(10) & 255) == 27 ) break;
	}

printf("\nPixel Blanc : %d",compteur);
printf("\nPixel Noir : %d", compteur2);
printf("\nTaille de la fenetre : %d x %d", result->width,  result->height);
printf("\nCoordonne doigt : %d x %d", x_result, y_result);


//Desalocation ressource
cvReleaseCapture( &capture );
cvDestroyWindow( "mywindow" );
cvDestroyWindow( "mywindow2" );
cvReleaseImage(&result);

return 1;
}
